#include <iostream>
#include "asio.hpp"

using asio::ip::udp;

std::string make_daytime_string()
{
    time_t now = time(0);
    return ctime(&now);
}

class udp_server
{
public:
    udp_server(asio::io_context& io_context, unsigned short port): socket_(io_context, udp::endpoint(udp::v4(), port))
    {
        start_receive();
    }

private:
    void start_receive()
    {
        socket_.async_receive_from(
            asio::buffer(recv_buffer_), remote_endpoint_,
            std::bind(&udp_server::handle_receive, this, std::placeholders::_1, std::placeholders::_2));
    }

    void handle_receive(const asio::error_code& error, std::size_t /*bytes_transferred*/)
    {
        if(!error)
        {
            std::shared_ptr<std::string> message(new std::string(make_daytime_string()));

            socket_.async_send_to(asio::buffer(*message), remote_endpoint_,
                                  std::bind(&udp_server::handle_send, this, message, std::placeholders::_1, std::placeholders::_2));

            start_receive();
        }
    }

    void handle_send(std::shared_ptr<std::string> /*message*/,
                     const asio::error_code& /*error*/,
                     std::size_t /*bytes_transferred*/)
    {
    }

    udp::socket socket_;
    udp::endpoint remote_endpoint_;
    std::array<char, 1> recv_buffer_;
};

int main(int argc, char* argv[])
{
    if(argc != 2)
    {
        std::cerr << "Usage: server <port>" << std::endl;
        return 1;
    }
    try
    {
        asio::io_context io_context;
        udp_server server(io_context, atoi(argv[1]));
        io_context.run();
    }
    catch(std::exception& e)
    {
        std::cerr << e.what() << std::endl;
    }

    return 0;
}